package clock.socoolby.com.clock.widget.animatorview.animator;

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;

import java.util.ArrayList;
import java.util.Iterator;

import clock.socoolby.com.clock.widget.animatorview.AbstractCacheDifferenceAnimator;
import clock.socoolby.com.clock.widget.animatorview.I_AnimatorEntry;

//引用自: https://blog.csdn.net/zhuxingchong/article/details/78342562
public class FireworkAnimator extends AbstractCacheDifferenceAnimator<FireworkAnimator.Firework, FireworkAnimator.Element> {

    private final String TAG = this.getClass().getSimpleName();

    public static final String NAME="Firework";


    private final static int MAX_SIZE = 2;

    public FireworkAnimator() {
        super(DYNAMIC_QUANTITY);
    }


    @Override
    public boolean run() {
        Iterator<Firework> iterator = list.iterator();
        while(iterator.hasNext()){
            Firework firework=iterator.next();
            if(firework.isNeedRemove()) {
                firework.stop();
                iterator.remove();
            }else
                firework.run();
        }
        if(list.size()>=MAX_SIZE)
            return true;
        int round=rand.nextInt(20);
        if(round>15)
            lunchFireWork(rand.nextInt(width),rand.nextInt(height),0,STYLE.HEART);//心型
        if(round>10)
            lunchFireWork(rand.nextInt(width),rand.nextInt(height),0,STYLE.MAX);//大烟花
        else if(round>5)
             lunchFireWork(rand.nextInt(width),rand.nextInt(height),0,STYLE.SMALL);//小烟花
        return true;
    }

    @Override
    public Firework createNewEntry() {
        return new Firework(new Location(rand.nextInt(width),rand.nextInt(height)),0,STYLE.MAX);
    }


    @Override
    public void stop() {
        for (Firework firework : list) {
            firework.stop();
        }
        super.stop();
    }

    public void lunchFireWork(float x, float y, int direction, STYLE mode) {
        final Firework firework = new Firework(new Location(x, y), direction, mode);
        list.add(firework);
    }


    private final static int MAX_DEFAULT_ELEMENT_COUNT = 200;//大烟花爆炸数量
    private int MAX_DEFAULT_DURATION = 30;
    private final static int MAX_DEFAULT_LAUNCH_SPEED = 25;
    private final static int MAX_DEFAULT_ELEMENT_SIZE = 8;

    private final static int MID_DEFAULT_ELEMENT_SIZE = 5;
    private final static int MID_DEFAULT_ELEMENT_COUNT = 40;//中烟花爆炸数量

    private final static int BIG_DEFAULT_ELEMENT_COUNT = 200;//大烟花爆炸数量
    private int BIG_DEFAULT_DURATION = 20;
    private final static float BIG_DEFAULT_LAUNCH_SPEED = 5;
    private final static float BIG_DEFAULT_ELEMENT_SIZE = 3;

    private final static int SMALL_DEFAULT_ELEMENT_COUNT = 8;//小星星爆炸数量
    private int SMALL_DEFAULT_DURATION = 10;//烟花引导持续时间
    private final static float SMALL_DEFAULT_LAUNCH_SPEED = 18;//烟花分散速度
    private final static float SMALL_DEFAULT_ELEMENT_SIZE = 8;//烟花颗粒大小

    private final static float DEFAULT_WIND_SPEED = 6;
    private final static float DEFAULT_GRAVITY = 6;

    public class Firework implements I_AnimatorEntry {
        final float screenWidthMeasure = 720;

        private Paint mPaint;
        private int count; // count of element
        private int duration;
        private float launchSpeed;
        private float windSpeed;
        private float gravity;
        private int windDirection; // 1 or -1
        private Location location;
        private float elementSize;

        //此值在爆炸前为引导时长，在爆炸后为存留时长
        private float animatorValue;

        private ArrayList<Element> elements = new ArrayList<>();

        private STYLE mode = STYLE.SMALL;

        public Firework(Location location, int windDirection, STYLE mode) {
            this.location = location;
            this.windDirection = windDirection;
            this.mode = mode;
            gravity = DEFAULT_GRAVITY;
            windSpeed = DEFAULT_WIND_SPEED;

            if(rand.nextInt(5)==1){
                count=rand.nextInt(MAX_DEFAULT_ELEMENT_COUNT);
                duration = rand.nextInt(MAX_DEFAULT_DURATION);
                if((mode == STYLE.MAX)) {
                    launchSpeed = rand.nextInt(new Float(BIG_DEFAULT_LAUNCH_SPEED).intValue());
                    elementSize = rand.nextInt(new Float(MID_DEFAULT_ELEMENT_SIZE).intValue());
                }else{
                    launchSpeed = rand.nextInt(MAX_DEFAULT_LAUNCH_SPEED);
                    elementSize = rand.nextInt(MAX_DEFAULT_ELEMENT_SIZE);
                }
            }else switch (mode){
                case MAX:// 大烟花
                    count = BIG_DEFAULT_ELEMENT_COUNT;
                    duration = BIG_DEFAULT_DURATION;
                    launchSpeed = BIG_DEFAULT_LAUNCH_SPEED;
                    elementSize = BIG_DEFAULT_ELEMENT_SIZE;
                break;
                case SMALL://小星星爆炸
                    count = SMALL_DEFAULT_ELEMENT_COUNT;
                    duration = SMALL_DEFAULT_DURATION;
                    launchSpeed = SMALL_DEFAULT_LAUNCH_SPEED;
                    elementSize = SMALL_DEFAULT_ELEMENT_SIZE;
                break;
                case HEART:
                    count = MID_DEFAULT_ELEMENT_COUNT;
                    duration = BIG_DEFAULT_DURATION;
                    launchSpeed = BIG_DEFAULT_LAUNCH_SPEED;
                    elementSize = BIG_DEFAULT_ELEMENT_SIZE;
                 break;
            }
            init();
            //Log.d("fireworkAnimator","duration:"+duration);
        }

        private void init() {
            Element temp=null;
            color=Util.roundColor();
            // 给每个火花设定一个随机的方向 0-360
            //timber.log.Timber.d("Firework start mode = " + mode + " count = " + count);
            switch (mode) {
                case  MAX:
                for (int i = 0; i < count; i++) {
                    temp=revectForTrashCache();
                    if(randColor)
                        randomColorIfAble();
                    if(temp==null)
                        temp=new Element(color, Math.toRadians(rand.nextInt(360)), rand.nextFloat() * launchSpeed,elementSize);
                    else{
                        temp.color=color;
                        temp.direction=Math.toRadians(rand.nextInt(360));
                        temp.speed=rand.nextFloat() * launchSpeed;
                        temp.r=elementSize;
                        temp.x=temp.y=0;
                    }
                    elements.add(temp);
                }
                break;
                case SMALL:
                for (int i = 0; i < count; i++) {
                    temp=revectForTrashCache();
                    if(randColor)
                        randomColorIfAble();
                    if(temp==null)
                        temp=new Element(color, Math.toRadians(rand.nextInt(360)), rand.nextFloat() * launchSpeed,elementSize);
                    else{
                        temp.color=color;
                        temp.direction=Math.toRadians(rand.nextInt(360));
                        temp.speed=rand.nextFloat() * launchSpeed;
                        temp.r=elementSize;
                        temp.x=temp.y=0;
                    }
                    elements.add(temp);
                }
                break;
                case HEART:
                    for (int i = 0; i < count; i++) {
                        temp=revectForTrashCache();
                        if(randColor)
                            randomColorIfAble();
                        if(temp==null)
                            temp=new Element();
                        temp.color=color;
                        //temp.direction=-count/2 * (((Math.sin(i)*Math.sqrt(Math.abs(Math.cos(i)))) / (Math.sin(i) + 1.4142)) - 2 * Math.sin(i) + 2);
                        temp.direction=Math.toRadians(rand.nextInt(360));
                        temp.speed=rand.nextFloat() * launchSpeed;
                        temp.r=elementSize;
                        temp.x=temp.y=0;
                        elements.add(temp);
                    }
                    break;
            }
            mPaint = new Paint();
            mPaint.setColor(Color.WHITE);
            timeCount = 1;
            animatorValue = duration;
        }

        private float timeCount = 1;
        private final float dif = 0.00816f;
        private boolean needRemove = false;

        public boolean isNeedRemove() {
            return needRemove;
        }

        boolean isStart = false;
        Element elementTemp=null;
        /*
         * 开始烟花爆炸动画
         */
        public void fire() {
            //Log.d("zxc55", "onAnimationUpdate animatorValue = " + animatorValue);
            // 计算每个火花的位置
            isStart = true;
            animatorValue = timeCount;
            switch (mode) {
                case HEART:
                    for (int i = 0; i < count; i++) {
                        elementTemp = elements.get(i);
                        //产生极坐标点
                        int m = i;
                        double n = -count / 2 * (((Math.sin(i) * Math.sqrt(Math.abs(Math.cos(i)))) / (Math.sin(i) + 1.4142)) - 2 * Math.sin(i) + 2);
                        //转换为笛卡尔坐标
                        elementTemp.x = new Double(n * Math.cos(m) + elementTemp.speed * animatorValue + windSpeed * windDirection).floatValue();
                        elementTemp.y = new Double(n * Math.sin(m) - elementTemp.speed * animatorValue + gravity * (1 - animatorValue)).floatValue();
                        //Log.d("fireworkAnimator", "element.percent:\t" + elementTemp.percent + "\telement.y:\t" + elementTemp.y);
                    }
                    break;
                default:
                    for (Element element : elements) {
                        element.x = (float) (element.x
                                + Math.cos(element.direction) * element.speed
                                * animatorValue + windSpeed * windDirection);
                        element.y = (float) (element.y
                                - Math.sin(element.direction) * element.speed
                                * animatorValue + gravity * (1 - animatorValue));
                    }
            }
        }

        public void stop(){
            moveToTrashCache(elements);
            isStart=false;
            needRemove=true;
        }

        public void onAnimationEnd() {
            //Log.d("zxc118", "onAnimationEnd clear fireworkAnimator");
            needRemove = true;
        }

        public void setDuration(int duration) {
            this.duration = duration;
        }

        public void run(){
            //前一部分为引导
            duration--;

            if(duration>0)
                return;

            if(duration==0) {
                fire();
            }
            /*
             * 有些情况小星星动画不能停止，强制结束
             */
            n++;
            if (n > maxTime) {
                onAnimationEnd();
            }

            /*
             * 更新烟花位置
             */
            if (n > 2 && isStart) {
                updateLocation();
            }
        }

        private final int maxTime = 38;
        private int n = 0;
        private float fraction=0;
        private float startX=0;
        private float startY=0;
        private float x=0;
        private float y=0;


        public void updateLocation() {
            animatorValue -= dif;
            if (animatorValue < 0) {
                onAnimationEnd();
            }
                for (Element element : elements) {
                    element.x = (float) (element.x
                            + Math.cos(element.direction) * element.speed
                            * animatorValue + windSpeed * windDirection);
                    element.y = (float) (element.y
                            - Math.sin(element.direction) * element.speed
                            * animatorValue + gravity * (1 - animatorValue));
                }
        }

        @Override
        public void move(int maxWidth, int maxHight) {

        }

        @Override
        public void onDraw(Canvas canvas, Paint mPaint) {
            if(isStart){
                mPaint.setAlpha((int) (225 * animatorValue));
                for (Element element : elements) {
                    element.draw(canvas,mPaint,location.x,location.y);
                }
            }else{
                fraction=duration/animatorValue;
                startX=width/2;
                if(location.y<(height/2)&&rand.nextBoolean())
                    startX=rand.nextInt(width);
                startY=height;
                x=location.x + fraction * (startX-location.x);
                y=location.y+fraction * (startY-location.y);
                //canvas.drawLine(width/2,height,percent,y,mPaint);
                canvas.drawCircle(x,y,MID_DEFAULT_ELEMENT_SIZE,mPaint);
                canvas.drawCircle(x+SMALL_DEFAULT_ELEMENT_SIZE/2,y,SMALL_DEFAULT_ELEMENT_SIZE,mPaint);
            }
        }

        @Override
        public void setAnimatorEntryColor(int color) {

        }
    }

    public enum STYLE{
        MAX,SMALL,HEART;
    }

    static class Location {
        public float x;
        public float y;

        public Location(float x, float y) {
            this.x = x;
            this.y = y;
        }
    }

    public class Element {
        public int color;
        public Double direction;
        public float speed;
        public float x = 0;
        public float y = 0;
        public float r=10;

        public Element(){

        }

        public Element(int color, Double direction, float speed,float r) {
            this.color = color;
            this.direction = direction;
            this.speed = speed;
            this.r=r;
        }

        public void draw(Canvas canvas, Paint mPaint,float baseX,float baseY) {
            mPaint.setColor(color);
            canvas.drawCircle(baseX + x, baseY + y,r, mPaint);
        }
    }
}
